﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Transactions;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity;
using Microsoft.Practices.Unity;
using Vallejos.Criptografia;
using Vallejos.Datos.Factory;
using Vallejos.Datos.Servicios;
using Vallejos.Entidades.Interfaces;
using Vallejos.Entidades.ModeloDeDatos;
using Vallejos.Excepciones;
using Vallejos.Entidades.Enums;

namespace Vallejos.Datos.Accesos
{
    public class SeguridadDAL : ISeguridad
    {
        private readonly DatabaseService _database;
        private readonly ComunDAL _comun;

        public SeguridadDAL()
        {
            var container = new UnityContainer()
                .AddNewExtension<EnterpriseLibraryCoreExtension>();
            _database = container.Resolve<DatabaseService>();
            _comun = new ComunDAL();
        }


        public Persona Autenticar(Usuario usuario)
        {
            Persona persona = null;
            Usuario usr = null;

            var factory = new UsuarioFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("Autenticar"))
                {
                    _database.AddInParameter(command, "Login", DbType.String, usuario.Login);
                    _database.AddInParameter(command, "Password", DbType.String,
                                             Encriptacion.CodificarDecodificar(usuario.Password));

                    using (var reader = _database.ExecuteReader(command))
                        if (reader.Read())
                            usr = factory.Crear(reader);

                    if(usr != null)
                    {
                        persona = LeerPersona(null, usr.Id);
                        persona.Usuario = usr;
                    }
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return persona;
        }

        public List<Ambito> LeerAmbitos(int idLenguaje)
        {
            var ambitos = new List<Ambito>();

            var factory = new AmbitoFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerAmbitos"))
                {
                    _database.AddInParameter(command, "Id_Lenguaje", DbType.Int32, idLenguaje);

                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                            ambitos.Add(factory.Crear(reader));
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return ambitos;
        }

        public List<Perfil> BuscarPerfiles(Perfil perfil, int idLenguaje)
        {
            return null;
        }

        public List<Perfil> BuscarPerfiles(Perfil perfil)
        {
            var perfiles = new List<Perfil>();

            var factory = new PerfilFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("BuscarPerfiles"))
                {
                    if (perfil.Ambito != null)
                        _database.AddInParameter(command, "Id_Ambito", DbType.Int32, perfil.Ambito.IdAmbito);
                    _database.AddInParameter(command, "Nombre", DbType.String, perfil.Nombre ?? String.Empty);
                    _database.AddInParameter(command, "Codigo", DbType.String, perfil.Codigo ?? String.Empty);
                    if (perfil.Concesionaria != null)
                        _database.AddInParameter(command, "Id_Concesionaria", DbType.Int32, perfil.Concesionaria.Id);

                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                            perfiles.Add(factory.Crear(reader));
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return perfiles;
        }


        public void GuardarPerfil(Perfil perfil)
        {
            try
            {
                using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew))
                {
                    using (var command = _database.GetStoredProcCommand("GuardarPerfil"))
                    {
                        
                        if (perfil.Id != 0)
                            _database.AddInParameter(command, "Id", DbType.Int32, perfil.Id);
                        else
                            _database.AddOutParameter(command, "Id", DbType.Int32, Int32.MaxValue);

                        _database.AddInParameter(command, "Codigo", DbType.String, perfil.Codigo);
                        _database.AddInParameter(command, "Nombre", DbType.String, perfil.Nombre);
                        _database.AddInParameter(command, "Id_Ambito", DbType.Int32, Convert.ToInt32(perfil.Ambito.IdAmbito));
                        if (perfil.Concesionaria != null)
                            _database.AddInParameter(command, "Id_Concesionaria", DbType.Int32, perfil.Concesionaria.Id);
                        _database.AddInParameter(command, "Descripcion", DbType.String, perfil.Descripcion);

                        _database.ExecuteNonQuery(command);

                        if (perfil.Id == 0)
                            perfil.Id = Convert.ToInt32(_database.GetParameterValue(command, "Id"));

                        if (perfil.Permisos.Count > 0)
                            AsociarPermisosAlPerfil(perfil);

                        scope.Complete();
                    }
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }
        }

        private void AsociarPermisosAlPerfil(Perfil perfil)
        {
            try
            {
                using (var scope = new TransactionScope(TransactionScopeOption.Required))
                {
                    using (var command = _database.GetStoredProcCommand("AsociarPermisoAlPerfil"))
                    {
                        _database.AddInParameter(command, "Id_Perfil", DbType.Int32, perfil.Id);
                        _database.AddInParameter(command, "Id_Permiso", DbType.Int32, 0);

                        foreach (var permiso in perfil.Permisos)
                        {
                            _database.SetParameterValue(command, "Id_Permiso", permiso.Id);
                            _database.ExecuteNonQuery(command);
                        }

                        scope.Complete();
                    }
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }
        }

        public List<Permiso> LeerPermisos(int idLenguaje)
        {
            var permisos = new List<Permiso>();
            var factory = new PermisoFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerPermisos"))
                {
                    _database.AddInParameter(command, "Id_Lenguaje", DbType.Int32, idLenguaje);

                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                            permisos.Add(factory.Crear(reader));
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return permisos;
        }


        public Perfil LeerPerfil(int idPerfil)
        {
            Perfil perfil = null;

            var factory = new PerfilFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerPerfil"))
                {
                    _database.AddInParameter(command, "Id_Perfil", DbType.Int32, idPerfil);

                    using (var reader = _database.ExecuteReader(command))
                        if (reader.Read())
                            perfil = factory.Crear(reader);
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return perfil;
        }

        public Usuario LeerUsuario(int idUsuario)
        {
            Usuario usuario = null;

            var factory = new UsuarioFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerUsuario"))
                {
                    _database.AddInParameter(command, "Id", DbType.Int32, idUsuario);

                    using (var reader = _database.ExecuteReader(command))
                        if (reader.Read())
                            usuario = factory.Crear(reader);
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return usuario;
        }

        public List<Permiso> LeerPermisos(Perfil perfil)
        {
            var permisos = new List<Permiso>();
            var factory = new PermisoFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerPermisosDePerfil"))
                {
                    _database.AddInParameter(command, "Id_Perfil", DbType.Int32, perfil.Id);

                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                            permisos.Add(factory.Crear(reader));
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return permisos;
        }


        public List<Persona> BuscarPersonas(Persona persona)
        {
            var personas = new List<Persona>();

            var factory = new PersonaFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("BuscarPersonas"))
                {
                    if (persona.Ambito != null)
                        _database.AddInParameter(command, "Id_Ambito", DbType.Int32, persona.Ambito.IdAmbito);

                    _database.AddInParameter(command, "Nombre", DbType.String, persona.DatosPersonales.Nombre);

                    if(persona.GetType() == typeof(Persona) && ((EmpleadoDeConcesionaria)persona).Concesionaria != null)
                        _database.AddInParameter(command, "Id_Concesionaria", DbType.Int32, ((EmpleadoDeConcesionaria)persona).Concesionaria.Id);

                    _database.AddInParameter(command, "Apellido", DbType.String, persona.DatosPersonales.Apellido);

                    if (persona.Usuario.Perfil != null)
                        _database.AddInParameter(command, "Id_Perfil", DbType.Int32, persona.Usuario.Perfil.Id);

                    if (persona.DatosPersonales.Sexo != null)
                        _database.AddInParameter(command, "Id_Sexo", DbType.Int32, Convert.ToInt32(persona.DatosPersonales.Sexo.IdSexo));

                    _database.AddInParameter(command, "Login", DbType.String, persona.Usuario.Login);


                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                            personas.Add(factory.Crear(reader));
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return personas;
        }

        public Persona LeerPersona(int idPersona)
        {
            return LeerPersona(idPersona, null);
        }

        private Persona LeerPersona(int? idPersona, int? idUsuario)
        {
            Persona persona = null;

            var factory = new PersonaFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerPersona"))
                {
                    if (idPersona != null)
                        _database.AddInParameter(command, "Id", DbType.Int32, idPersona);
                    else
                        _database.AddInParameter(command, "Id_Usuario", DbType.Int32, idUsuario);

                    using (var reader = _database.ExecuteReader(command))
                        if (reader.Read())
                        {
                            persona = factory.Crear(reader);
                            persona.Usuario = LeerUsuario(persona.Usuario.Id);
                        }
                            
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return persona;
        }


        public Persona GuardarPersona(Persona persona)
        {
            try
            {
                using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew))
                {
                    using (var command = _database.GetStoredProcCommand("GuardarPersona"))
                    {

                        if (persona.Id != 0)
                            _database.AddInParameter(command, "Id", DbType.Int32, persona.Id);
                        else
                            _database.AddOutParameter(command, "Id", DbType.Int32, Int32.MaxValue);

                        persona.Usuario = GuardarUsuario(persona.Usuario);
                        _database.AddInParameter(command, "Id_Usuario", DbType.String, persona.Usuario.Id);

                        _database.AddInParameter(command, "InstanciaPersona", DbType.Int32, (int)persona.Ambito.IdAmbito);

                        persona.DatosPersonales = _comun.GuardarInformacionPersonal(persona.DatosPersonales);
                        _database.AddInParameter(command, "Id_DatosPersonales", DbType.String, persona.DatosPersonales.Id);

                        persona.DatosDomiciliarios = _comun.GuardarInformacionDomiciliaria(persona.DatosDomiciliarios);
                        _database.AddInParameter(command, "Id_DatosDomiciliarios", DbType.String, persona.DatosDomiciliarios.Id);

                        if(persona.GetType() == typeof(Cliente))
                        {
                            var cliente = persona as Cliente;
                            if(cliente.DatosDeEmpresa != null)
                            {
                                cliente.DatosDeEmpresa = _comun.GuardarInformacionDeEmpresa(cliente.DatosDeEmpresa);
                                _database.AddInParameter(command, "Id_DatosDeEmpresa", DbType.String, cliente.DatosDeEmpresa.Id);
                            }
                        }
                        else if (persona.GetType() == typeof(EmpleadoDeConcesionaria))
                        {
                            var empleado = persona as EmpleadoDeConcesionaria;

                            _database.AddInParameter(command, "Id_Concesionaria", DbType.Int32, empleado.Concesionaria.Id);
                            _database.AddInParameter(command, "Legajo", DbType.Int32, empleado.Legajo);
                        }
                        else
                            _database.AddInParameter(command, "Legajo", DbType.Int32, ((EmpleadoBancario)persona).Legajo);

                        _database.ExecuteNonQuery(command);

                        if (persona.Id == 0)
                            persona.Id = Convert.ToInt32(_database.GetParameterValue(command, "Id"));
                    }
                    scope.Complete();
                }

                return persona;
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }
        }

        private Usuario GuardarUsuario(Usuario usuario)
        {
            try
            {
                using (var command = _database.GetStoredProcCommand("GuardarUsuario"))
                {
                    if (usuario.Id != 0)
                        _database.AddInParameter(command, "Id", DbType.Int32, usuario.Id);
                    else
                        _database.AddOutParameter(command, "Id", DbType.Int32, Int32.MaxValue);

                    _database.AddInParameter(command, "Login", DbType.String, usuario.Login);
                    _database.AddInParameter(command, "Password", DbType.String, Encriptacion.CodificarDecodificar(usuario.Password));
                    _database.AddInParameter(command, "Activo", DbType.Boolean, usuario.Activo);
                    _database.AddInParameter(command, "Email", DbType.String, usuario.Email);
                    _database.AddInParameter(command, "Id_Perfil", DbType.Int32, usuario.Perfil.Id);

                    _database.ExecuteNonQuery(command);

                    if(usuario.Id == 0)
                        usuario.Id = Convert.ToInt32(_database.GetParameterValue(command, "Id"));
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }
            return usuario;
        }



        #region ISeguridad Members


        public List<Menu> LeerMenusDeUsuario(int idUsuario, int idLenguaje)
        {
            var menues = new List<Menu>();

            var factory = new MenuFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerMenuDeUsuario"))
                {
                    _database.AddInParameter(command, "Id_Usuario", DbType.Int32, idUsuario);
                    _database.AddInParameter(command, "Id_Lenguaje", DbType.Int32, idLenguaje);

                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                            menues.Add(factory.Crear(reader));
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return menues;
        }

        #endregion
    }

}